package com.imooc.course.server.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.imooc.course.server.dao.ShortMessagingServiceDao;
import com.imooc.course.server.dto.ShortMessagingServiceDto;
import com.imooc.course.server.dto.PageDto;
import com.imooc.course.server.entity.ShortMessagingService;
import com.imooc.course.server.entity.ShortMessagingServiceExample;
import com.imooc.course.server.enums.BusinessExceptionEnum;
import com.imooc.course.server.enums.SmsStatusEnum;
import com.imooc.course.server.enums.SmsUseEnum;
import com.imooc.course.server.exception.BusinessException;
import com.imooc.course.server.service.ShortMessagingServiceService;
import com.imooc.course.server.utils.CopyUtil;
import com.imooc.course.server.utils.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

@Service("shortMessagingServiceService")
public class ShortMessagingServiceServiceImpl implements ShortMessagingServiceService {

    private static final Logger log = LoggerFactory.getLogger(ShortMessagingServiceServiceImpl.class);

    @Resource(name = "shortMessagingServiceDao")
    private ShortMessagingServiceDao shortMessagingServiceDao;

    /**
     * 列表查询
     * @param pageDto
     */
    @Override
    public void getList(PageDto pageDto) {
        PageHelper.startPage(pageDto.getPage(),pageDto.getSize());

        ShortMessagingServiceExample shortMessagingServiceExample = new ShortMessagingServiceExample();
        List<ShortMessagingService> shortMessagingServiceList = shortMessagingServiceDao.selectByExample(shortMessagingServiceExample);

        PageInfo<ShortMessagingService> shortMessagingServicePageInfo = new PageInfo<>(shortMessagingServiceList);
        pageDto.setTotal(shortMessagingServicePageInfo.getTotal());

        List<ShortMessagingServiceDto> shortMessagingServiceDtoList = CopyUtil.copyList(shortMessagingServiceList, ShortMessagingServiceDto.class);

        pageDto.setList(shortMessagingServiceDtoList);
    }

    /**
     * 保存，id有值为更新，id无值为新增
     * @param shortMessagingServiceDto
     */
    @Override
    public void save(ShortMessagingServiceDto shortMessagingServiceDto) {
        ShortMessagingService copy = CopyUtil.copy(shortMessagingServiceDto, ShortMessagingService.class);
        String id = shortMessagingServiceDto.getId() != null ? shortMessagingServiceDto.getId().trim() : shortMessagingServiceDto.getId();
        if (StringUtils.isEmpty(id)) {
            this.addShortMessagingService(copy);
        }else{
            this.updateShortMessagingService(copy);
        }
    }

    private void updateShortMessagingService(ShortMessagingService copy) {
        shortMessagingServiceDao.updateByPrimaryKeySelective(copy);
    }

    private void addShortMessagingService(ShortMessagingService copy) {
        copy.setId(UuidUtil.getShortUuid());
        shortMessagingServiceDao.insertSelective(copy);
    }

    /**
     * 删除
     * @param id
     */
    @Override
    public void delete(String id) {
        shortMessagingServiceDao.deleteByPrimaryKey(id);
    }

    /**
     * 发送短信验证码
     * 同手机号同操作1分钟内不能重复发送短信
     * @param smsDto
     */
    @Override
    public void sendCode(ShortMessagingServiceDto smsDto) {
        //查找1分钟内，同一个手机号码，同样用途，并且没有被使用的手机号码
        ShortMessagingServiceExample example = newSMSExample();
        // 这个date由前端传来，然后再减去1分钟
        // 同时，短信服务数据表中的短信生成时间也应该和前端持一致。
        // new一个date获取上一分钟的时间并封装到一个新的date对象里面去。
        Date date = new Date(smsDto.getMillisecond() - 1 * 60 * 1000);
        example.createCriteria().andAtGreaterThan(date)
                .andMobileEqualTo(smsDto.getMobile())
                .andUseEqualTo(smsDto.getUse())
                .andStatusEqualTo(SmsStatusEnum.NOT_USED.getCode());
        List<ShortMessagingService> smsList = shortMessagingServiceDao.selectByExample(example);
        if (smsList == null || smsList.size() == 0) {
            // 同一手机号码，之前未使用的同一用途的短信，都设置为已过期
            // 保证只有一条，同一手机号码，同一用途的，未使用的，短信验证码
            ShortMessagingServiceExample smsExample = newSMSExample();
            smsExample.createCriteria().andMobileEqualTo(smsDto.getMobile()).andUseEqualTo(smsDto.getUse());
            ShortMessagingService sms = new ShortMessagingService();
            sms.setStatus(SmsStatusEnum.USED.getCode());
            shortMessagingServiceDao.updateByExampleSelective(sms,smsExample);
            // 产生新的短信，存入数据库并调用第三方短信发送接口
            this.saveAndSend(smsDto);
        } else {
            throw new BusinessException(BusinessExceptionEnum.SHORT_MESSAGING_TOO_FREQUENT);
        }
    }

    private ShortMessagingServiceExample newSMSExample() {
        return new ShortMessagingServiceExample();
    }

    private void saveAndSend(ShortMessagingServiceDto smsDto) {
        // 生成6位数字
        String code = String.valueOf((int)(((Math.random() * 9) + 1) * 100000));
        smsDto.setCode(code);
        smsDto.setStatus(SmsStatusEnum.NOT_USED.getCode());
        smsDto.setAt(new Date(smsDto.getMillisecond()));
        ShortMessagingService sms = CopyUtil.copy(smsDto, ShortMessagingService.class);
        this.addShortMessagingService(sms);
        // 短信服务数据表中的短信生成时间也应该和前端传来的时间date保持一致。
        // 定时任务，在前端传来的时间date的基础上加上5分钟，5分钟后将这条生成的验证码设置为已过期。
        this.afterAWhile(5L * 60L * 1000L, smsDto);


        // TODO 调第三方短信接口发送短信
        // coding...

    }

    private void afterAWhile(Long timeInterval, ShortMessagingServiceDto smsDto) {

        Runnable runnable = new Runnable() {
            public void run() {
                try {
                    // 线程沉睡timeInterval(秒)后执行
                    // sleep()：同步延迟数据，并且会阻塞线程
                    Thread.sleep(timeInterval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // ------- 要运行的任务代码
                ShortMessagingServiceExample smsExample = newSMSExample();
                smsExample.createCriteria()
                        .andMobileEqualTo(smsDto.getMobile())
                        .andUseEqualTo(smsDto.getUse())
                        .andCodeEqualTo(smsDto.getCode())
                        .andStatusEqualTo(smsDto.getStatus())
                        .andAtEqualTo(smsDto.getAt());
                ShortMessagingService sms = new ShortMessagingService();
                sms.setStatus(SmsStatusEnum.USED.getCode());
                int i = shortMessagingServiceDao.updateByExampleSelective(sms, smsExample);
                log.info("当前时间：{}", new Date());
                log.info("执行线程，"+i+"条手机短信被设置为已使用：");
                log.info("手机号码：{}，短信：{}，用途：{}", smsDto.getMobile(), smsDto.getCode(), smsDto.getUse());
                // ------- ends here
            }
        };
        //创建线程，线程充当定时器
        Thread thread = new Thread(runnable);
        //开始执行
        log.info("线程沉睡" + timeInterval / 1000 / 60 + "分钟，当前时间：{}", smsDto.getAt());
        thread.start();
    }


    /**
     * 校验短信验证码
     * 榆次同手机号同操作5分钟内的短信
     * @param smsDto
     */
    @Override
    public void validateShortMessage(ShortMessagingServiceDto smsDto) {
        //查找5分钟内，同一个手机号码，同样用途，并且没有被使用的手机号码
        ShortMessagingServiceExample example = new ShortMessagingServiceExample();
        // new一个date获取上一分钟的时间并封装到一个新的date对象里面去。
        Date date = new Date(new Date().getTime() - 5 * 60 * 1000);
        example.createCriteria().andAtGreaterThan(date)
                .andMobileEqualTo(smsDto.getMobile())
                .andUseEqualTo(smsDto.getUse())
                .andStatusEqualTo(SmsStatusEnum.NOT_USED.getCode());
        List<ShortMessagingService> smsList = shortMessagingServiceDao.selectByExample(example);
        if (CollectionUtils.isEmpty(smsList)) {
            log.info("短信验证码已过期", smsDto);
            throw new BusinessException(BusinessExceptionEnum.SHORT_MESSAGING_EXPIRED);
        } else {
            ShortMessagingService smsDb = smsList.get(0);
            if (smsDb.getCode().equals(smsDto.getCode())) {
                ShortMessagingService sms = new ShortMessagingService();
                sms.setId(smsDb.getId());
                sms.setStatus(SmsStatusEnum.USED.getCode());
                shortMessagingServiceDao.updateByPrimaryKeySelective(sms);
            } else {
                log.info("短信验证码不正确，用户 = {}，数据库 = {}", smsDto.getCode(),smsDb.getCode());
                throw new BusinessException(BusinessExceptionEnum.SHORT_MESSAGING_ERROR);
            }
        }
    }
}
